/*
    This file is part of qmobex project.
    Copyright (C) 2011  Piotr (apito) Adamczuk <adamczuk.piotr@gmail.com>

    Qmobex is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */
#ifndef MOBEX_H
#define MOBEX_H
#include <QObject>
#include <QByteArray>
#include <QString>
#include <QHash>
#include "posix_qextserialport.h"

/*!
\mainclass MObex

MObex Class is designed for connectitng wiht Samsung SGH phones (eg. u800) on Linux using m-obex protocol via USB Cable. This protocol is used by Samsung PcStudio.\n
Requires QtCore4 libraries (tested on 4.6) and posix_qextserialport library.

\todo For now this class can be used only for manipulating contacts.\n
More sniffing must be done for calendar, SMS, and File transfer.

\version 0.1
\author Piotr (apito) Adamczuk <adamczuk.piotr@gmail.com>
*/
class MObex : public QObject{
    Q_OBJECT
public:
    /*!
    \enum Mmode

    Mode in wich Object is.\n
    \sa currentMode(),  modeChanged();
    */
    enum Mmode
    {
        /** -2: Port is locked by other query.*/
        LOCKED = -2,
        /** -1: Cannot read and write to given port.*/
        NO_DEVICE = -1,
        /**  0: Port is open but sending AT Commands has no effect. */
        INVALID = 0,
        /** 1: AT mode. Object can send and receive AT commands. */
        AT = 1,
        /** 2: MOBEX connected to M-OBEX server on phone (It's we need) */
        OBEX = 2
           };

    /*!
      \fn MObex();
      Default constructor.
      Will set port to \b /dev/ttyACM0.\n
      Mode will be Mmode::NO_DEVICE.\n
      Use openPort() to connect to this port or openPort(QString &newPort).
      \sa ~MObex(), openPort(), openPort(QStirng &newPort)
      */
    MObex();

    /*!
      \fn MObex(QString &portName);
      Construct object and set new port\n
      Will set port to \b portName \n
       Mode will be Mmode::NO_DEVICE. Use openPort() to connect to this port
      \param portName to open.
      \sa MObex(), openPort()
      */
    MObex(QString &portName);

    /*!
      \fn ~MObex();
      Default destructor.
      Will close port.
      */
    ~MObex();

    /*!
      \fn QString currentPort();
      \return Current port name.
      */
    QString currentPort() {
        return port->portName();
    }

    /*!
      \fn int currentMode();
      \return Mode in wich object is
      \sa Mmode
      */
    int currentMode() {
        return mode;
    }

    /*!
    \fn bool isObex();
    Check if object is in OBEX mode
    \return True if object is in OBEX mode, otherwise return False
    \sa currentMode()
    */
    bool isObex();

    /*!
      \fn bool contactsCount(int &total, int &max, QByteArray memory = MEMORY_PHONE)
        Check number of total and max number of contacts for specified memory and set value in referenced integers.
        \param total Reference to total number of contacts.
        \param max Reference to maximum number of contacts.
        \param memory Defines memory for check PHONE or SIM.
        \return True on succes and False on Failure.
      */
    bool contactsCount(int &total, int &max, QByteArray memory = MObex::MEMORY_PHONE );

    /*!
    \fn QByteArray contactsLoad(QHash<int, QByteArray> &contacts, int partToGet, QByteArray memory = MObex::MEMORY_PHONE)
    Loads Contacts from given memory to referenced QHash obeject.\n
    Integer key of QHash contacts object is contact ID in Memory\n
    QByteArray is vCard2.1 with contact details.
    \param contacts QHash object to store downloaded contacts.
    \param partToGet Integer value specified what part of contacts to download. Starting with 0.
    \param memory Defines memory for loading: PHONE or SIM.
    \return Status of loading defined in Response.params.
    \note User must check returned value. Normally phone will send 10 vCards. If there is more contacts then returned value will be MObex::PARAMS_NEXT (0x0002). User should then increment \b partToGet every time and wait for MObex::PARAMS_OK response.\n
    */
    QByteArray contactsLoad(QHash<int, QByteArray> &contacts, int partToGet, QByteArray memory = MObex::MEMORY_PHONE);

    /*!
      \fn int contactsCreate( QByteArray const &data, QByteArray const memory = MEMORY_PHONE);
    Create new contact in specified Memory
    \param data Contacts details as vCard2.1
    \param memory Storage memory. PHONE or SIM
    \return ID of new contact or -2 on failure or -1 on operation error
    \note ID can be 0. It's first entry in phonebook.
    */
    int contactsCreate(QByteArray const &data, QByteArray const memory = MObex::MEMORY_PHONE);

    /*!
      \fn int contactsWrite( QByteArray const &data, int contactID, QByteArray const memory = MEMORY_PHONE);
    Replace contact details with given contactID in specified Memory
    \param data Contacts details as vCard2.1
    \param contactID ID of contact in memory returned by MObex::contactsLoad() function.
    \param memory Storage memory. PHONE or SIM
    \return 0 on Success, -2 on Query Error and other result on Operation errors
    \todo Find error codes.
    */
    int contactsWrite(QByteArray const &data, int contactID, QByteArray const memory = MObex::MEMORY_PHONE);


    /*!
      \fn int contactsDelete(int contactID, QByteArray const memory = MEMORY_PHONE);
    Delete contact with given contactID in specified Memory
    \param contactID ID of contact in memory returned by MObex::contactsLoad() function.
    \param memory Storage memory. PHONE or SIM (Default PHONE)
    \return \b 0 on Success and non 0 value on error;
    \todo Find error codes.
    \note code 4 may be not found
    */
    int contactsDelete(int contactID, QByteArray const memory = MObex::MEMORY_PHONE);

    /*!
      \fn QString checkModel();
          Check Phone Model Name using AT commands. Object must be in MObex::AT. If object is in other mode will return empty QString.
      \return Founded phone model name, "Unknown" or empty string.
      */
     QString checkModel();


    //Static Public Members

    /*!
    \b 0x01
     \n Phone Memory
    */
    static const QByteArray MEMORY_PHONE;

    /*!
    \b 0x02
    \n SIM Card Memory
    */
    static const QByteArray MEMORY_SIM;

    /*!
    \b 0x0000
    \n All should be OK.
    */
    static const QByteArray PARAMS_OK;

    /*!
    \b 0x0001
    \n There was Error. For Example while loading contacts or counting them.
    \sa contactsCount(), contactsLoad(), contactsCreate()
    */
    static const QByteArray PARAMS_ERROR;

    /*!
    \b 0x0002
    \n When loading contacts means that this is only part of data.
    \sa contactsLoad()
    */
    static const QByteArray PARAMS_NEXT;



public slots:
    /*!
      Open port to AT mode. If port is open close it and reconnect.\n
      It's also Qt slot.
      \param portPath Port to connect. By default /dev/ttyACM0.
      \return Value returned by MObex::currentMode()
      \note User MUST start OBEX session himself by running MObex::ObexStart().
      \sa ObexStart();
    */
    int openPort(QString portPath);

    /*!
      \fn void closePort();
      Close connection to port.\n
      It's also Qt slot.
      */
    void closePort();

   /*!
     \fn void ObexStart();
     Try to start MOBEX Session.\n
     User should listen for modeChanged(int) SIGNAL or Check currentMode() manually after calling this funcion.
     */
    void ObexStart();


    void checkState();

signals:
    /*!
      \fn void modeChanged(int currentMode);
      Emit current mode after mode changed
      */
    void modeChanged(int currentMode);


private:
    //Computer serial port for reading and writing
    //For now uses QestSerialPort (Linux part only)
    //In future may be replaced by standart Qt I/O device
    Posix_QextSerialPort *port;
    /*
     OBJET MODES
     int MObex object mode.
     @ accesed by public:currentMode()

     */
    /*!
      \property mode
      Holds current mode of object
      \sa currentMode(), modeChanged();
    */
    int mode;
    //change mode end emit signal with current mode
    void setMode(int newMode);

    //will lock reading and writeing to port in future
    volatile bool locked;

    //MOBEX SESSION ID
    QByteArray SID;
    /*
      *Create MOBEX request
      return header
    */
    /*!
      \fn QByteArray convertSize(int value, int byteSize = 2);
      Change integer value to hex encoded QByteArray of given size.
      \param value Integer value to change for hex encoded QByteArray
      \param byteSize Default 2. Result will be size of byteSize with leadnigs 0;
      \return  QByteArray with size = byteSize and leadings 0
      \note  convertSize(4, 4) return 0x00000004
      */
     static  QByteArray convertSize(int value, int byteSize=2){
        QByteArray out = QByteArray::number(value, 16).rightJustified(byteSize*2,'0');
        return QByteArray::fromHex(out);
    }
    static int convertSize(QByteArray value) {
        return value.toHex().toInt(0,16);
    }
    void parseCards(QByteArray &data, QHash<int, QByteArray> &storeTable);

    QByteArray makeHeader( QByteArray const &typeconst,  QByteArray const  &command,  QByteArray const &params, const int dataSize = 0);
    /*
     response object
     */
    struct Response {
        QByteArray code;
        int size; //Size of all response package
        QByteArray params; //Additional parameters
        QByteArray command;//Command used
        int data_size; //size of data in package
        int raw_data_size;//for now this same as data size
    };
    //get response and load data
    Response getResponse(QByteArray &data);





private:
    bool ObexStop();

    static const QByteArray NL;
    static const QByteArray AT_MODEL;
    static const QByteArray AT_RESET;
    static const QByteArray AT_RESET_OK;
    static const QByteArray AT_INFO;
    static const QByteArray AT_OBEX_START;
    static const QByteArray AT_OBEX_START_OK;

    static const QByteArray DATA_START;     //0x49 next two bytes: size of data + 3
    static const QByteArray DATA_SIZE;      //0xC3 next four bytes: size of data
    static const QByteArray COMMAND_START;  //0x42 next two bytes: size of command + 3
    static const QByteArray PARAM_START;    //0x4C next two bytes: size of params +3
    static const QByteArray OBEX_INIT;
    static const QByteArray OBEX_INIT_OK;
    static const QByteArray OBEX_EXIT;
    static const QByteArray OBEX_CHECK;     //0x830003


    /*!
    \b  0x83;
   \n M-OBEX Request GET Code.
   */
    static const QByteArray OBEX_GET;

    /*!
    \b  0x82;
   \n M-OBEX Request PUT Final Code.
   */
    static const QByteArray OBEX_PUT;

    /*!
    \b  0xA0;
   \n M-OBEX Response OK Code.
   */
    static const QByteArray OBEX_OK;
    //QUERIES
    /*!
    \b m-obex/contacts/load
    */
    static const QByteArray CONTACTS_LOAD;

    /*!
     \b m-obex/contacts/count
    */
    static const QByteArray CONTACTS_COUNT;

    /*!
     \b m-obex/contacts/create
    */
    static const QByteArray CONTACTS_CREATE;

    /*!
      \b m-obex/contacts/write
    */
    static const QByteArray CONTACTS_WRITE;

    /*!
      \b m-obex/contacts/delete
    */
    static const QByteArray CONTACTS_DELETE;

};

#endif // MOBEX_H
